home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Blender 2.49b / blender-2.49b-windows.exe / $_4_ / .blender / scripts / help_browser.py < prev    next >
Text File  |  2009-08-31  |  20KB  |  815 lines

  1. #!BPY
  2.  
  3. """
  4. Name: 'Scripts Help Browser'
  5. Blender: 234
  6. Group: 'Help'
  7. Tooltip: 'Show help information about a chosen installed script.'
  8. """
  9.  
  10. __author__ = "Willian P. Germano"
  11. __version__ = "0.3 01/21/09"
  12. __email__ = ('scripts', 'Author, wgermano:ig*com*br')
  13. __url__ = ('blender', 'blenderartists.org')
  14.  
  15. __bpydoc__ ="""\
  16. This script shows help information for scripts registered in the menus.
  17.  
  18. Usage:
  19.  
  20. - Start Screen:
  21.  
  22. To read any script's "user manual" select a script from one of the
  23. available category menus.  If the script has help information in the format
  24. expected by this Help Browser, it will be displayed in the Script Help
  25. Screen.  Otherwise you'll be offered the possibility of loading the chosen
  26. script's source file in Blender's Text Editor.  The programmer(s) may have
  27. written useful comments there for users.
  28.  
  29. Hotkeys:<br>
  30.    ESC or Q: [Q]uit
  31.  
  32. - Script Help Screen:
  33.  
  34. This screen shows the user manual page for the chosen script. If the text
  35. doesn't fit completely on the screen, you can scroll it up or down with
  36. arrow keys or a mouse wheel.  There may be link and email buttons that if
  37. clicked should open your default web browser and email client programs for
  38. further information or support.
  39.  
  40. Hotkeys:<br>
  41.    ESC: back to Start Screen<br>
  42.    Q:   [Q]uit<br>
  43.    S:   view script's [S]ource code in Text Editor<br>
  44.    UP, DOWN Arrows and mouse wheel: scroll text up / down
  45. """
  46.  
  47. # $Id: help_browser.py 18607 2009-01-21 15:45:31Z ianwill $
  48. #
  49. # --------------------------------------------------------------------------
  50. # ***** BEGIN GPL LICENSE BLOCK *****
  51. #
  52. # Copyright (C) 2004: Willian P. Germano, wgermano _at_ ig.com.br
  53. #
  54. # This program is free software; you can redistribute it and/or
  55. # modify it under the terms of the GNU General Public License
  56. # as published by the Free Software Foundation; either version 2
  57. # of the License, or (at your option) any later version.
  58. #
  59. # This program is distributed in the hope that it will be useful,
  60. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  61. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  62. # GNU General Public License for more details.
  63. #
  64. # You should have received a copy of the GNU General Public License
  65. # along with this program; if not, write to the Free Software Foundation,
  66. # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  67. #
  68. # ***** END GPL LICENCE BLOCK *****
  69. # --------------------------------------------------------------------------
  70. # Thanks: Brendon Murphy (suggestion) and Kevin Morgan (implementation)
  71. # for the "run" button; Jean-Michel Soler for pointing a parsing error
  72. # with multilines using triple single quotes.
  73.  
  74. import Blender
  75. from Blender import sys as bsys, Draw, Window, Registry
  76.  
  77. WEBBROWSER = True
  78. try:
  79.     import webbrowser
  80. except:
  81.     WEBBROWSER = False
  82.  
  83. DEFAULT_EMAILS = {
  84.     'scripts': ['Bf-scripts-dev', 'bf-scripts-dev@blender.org']
  85. }
  86.  
  87. DEFAULT_LINKS = {
  88.     'blender': ["blender.org\'s Python forum", "http://www.blender.org/modules.php?op=modload&name=phpBB2&file=viewforum&f=9"]
  89. }
  90.  
  91. PADDING = 15
  92. COLUMNS = 1
  93. TEXT_WRAP = 100
  94. WIN_W = WIN_H = 200
  95. SCROLL_DOWN = 0
  96.  
  97. def screen_was_resized():
  98.     global WIN_W, WIN_H
  99.  
  100.     w, h = Window.GetAreaSize()
  101.     if WIN_W != w or WIN_H != h:
  102.         WIN_W = w
  103.         WIN_H = h
  104.         return True
  105.     return False
  106.  
  107. def fit_on_screen():
  108.     global TEXT_WRAP, PADDING, WIN_W, WIN_H, COLUMNS
  109.  
  110.     COLUMNS = 1
  111.     WIN_W, WIN_H = Window.GetAreaSize()
  112.     TEXT_WRAP = int((WIN_W - PADDING) / 6)
  113.     if TEXT_WRAP < 40:
  114.         TEXT_WRAP = 40
  115.     elif TEXT_WRAP > 100:
  116.         if TEXT_WRAP > 110:
  117.             COLUMNS = 2
  118.             TEXT_WRAP /= 2
  119.         else: TEXT_WRAP = 100
  120.  
  121. def cut_point(text, length):
  122.     "Returns position of the last space found before 'length' chars"
  123.     l = length
  124.     c = text[l]
  125.     while c != ' ':
  126.         l -= 1
  127.         if l == 0: return length # no space found
  128.         c = text[l]
  129.     return l
  130.  
  131. def text_wrap(text, length = None):
  132.     global TEXT_WRAP
  133.  
  134.     wrapped = []
  135.     lines = text.split('<br>')
  136.     llen = len(lines)
  137.     if llen > 1:
  138.         if lines[-1] == '': llen -= 1
  139.         for i in range(llen - 1):
  140.             lines[i] = lines[i].rstrip() + '<br>'
  141.         lines[llen-1] = lines[llen-1].rstrip()
  142.  
  143.     if not length: length = TEXT_WRAP
  144.  
  145.     for l in lines:
  146.         while len(l) > length:
  147.             cpt = cut_point(l, length)
  148.             line, l = l[:cpt], l[cpt + 1:]
  149.             wrapped.append(line)
  150.         wrapped.append(l)
  151.     return wrapped
  152.  
  153. def load_script_text(script):
  154.     global PATHS, SCRIPT_INFO
  155.  
  156.     if script.userdir:
  157.         path = PATHS['uscripts']
  158.     else:
  159.         path = PATHS['scripts']
  160.  
  161.     fname = bsys.join(path, script.fname)
  162.  
  163.     source = Blender.Text.Load(fname)
  164.     if source:
  165.         Draw.PupMenu("File loaded%%t|Please check the file \"%s\" in the Text Editor window" % source.name)
  166.  
  167.  
  168. # for theme colors:
  169. def float_colors(cols):
  170.     return map(lambda x: x / 255.0, cols)
  171.  
  172. # globals
  173.  
  174. SCRIPT_INFO = None
  175.  
  176. PATHS = {
  177.     'home': Blender.Get('homedir'),
  178.     'scripts': Blender.Get('scriptsdir'),
  179.     'uscripts': Blender.Get('uscriptsdir')
  180. }
  181.  
  182. if not PATHS['home']:
  183.     errmsg = """
  184. Can't find Blender's home dir and so can't find the
  185. Bpymenus file automatically stored inside it, which
  186. is needed by this script.  Please run the
  187. Help -> System -> System Information script to get
  188. information about how to fix this.
  189. """
  190.     raise SystemError, errmsg
  191.  
  192. BPYMENUS_FILE = bsys.join(PATHS['home'], 'Bpymenus')
  193.  
  194. f = file(BPYMENUS_FILE, 'r')
  195. lines = f.readlines()
  196. f.close()
  197.  
  198. AllGroups = []
  199.  
  200. class Script:
  201.  
  202.     def __init__(self, data):
  203.         self.name = data[0]
  204.         self.version = data[1]
  205.         self.fname = data[2]
  206.         self.userdir = data[3]
  207.         self.tip = data[4]
  208.  
  209. # End of class Script
  210.  
  211.  
  212. class Group:
  213.  
  214.     def __init__(self, name):
  215.         self.name = name
  216.         self.scripts = []
  217.  
  218.     def add_script(self, script):
  219.         self.scripts.append(script)
  220.  
  221.     def get_name(self):
  222.         return self.name
  223.  
  224.     def get_scripts(self):
  225.         return self.scripts
  226.  
  227. # End of class Group
  228.  
  229.  
  230. class BPy_Info:
  231.  
  232.     def __init__(self, script, dict):
  233.  
  234.         self.script = script
  235.  
  236.         self.d = dict
  237.  
  238.         self.header = []
  239.         self.len_header = 0
  240.         self.content = []
  241.         self.len_content = 0
  242.         self.spaces = 0
  243.         self.fix_urls()
  244.         self.make_header()
  245.         self.wrap_lines()
  246.  
  247.     def make_header(self):
  248.  
  249.         sc = self.script
  250.         d = self.d
  251.  
  252.         header = self.header
  253.  
  254.         title = "Script: %s" % sc.name
  255.         version = "Version: %s for Blender %1.2f or newer" % (d['__version__'],
  256.             sc.version / 100.0)
  257.  
  258.         if len(d['__author__']) == 1:
  259.             asuffix = ':'
  260.         else: asuffix = 's:'
  261.  
  262.         authors = "%s%s %s" % ("Author", asuffix, ", ".join(d['__author__']))
  263.  
  264.         header.append(title)
  265.         header.append(version)
  266.         header.append(authors)
  267.         self.len_header = len(header)
  268.  
  269.  
  270.     def fix_urls(self):
  271.  
  272.         emails = self.d['__email__']
  273.         fixed = []
  274.         for a in emails:
  275.             if a in DEFAULT_EMAILS.keys():
  276.                 fixed.append(DEFAULT_EMAILS[a])
  277.             else:
  278.                 a = a.replace('*','.').replace(':','@')
  279.                 ltmp = a.split(',')
  280.                 if len(ltmp) != 2:
  281.                     ltmp = [ltmp[0], ltmp[0]]
  282.                 fixed.append(ltmp)
  283.  
  284.         self.d['__email__'] = fixed
  285.  
  286.         links = self.d['__url__']
  287.         fixed = []
  288.         for a in links:
  289.             if a in DEFAULT_LINKS.keys():
  290.                 fixed.append(DEFAULT_LINKS[a])
  291.             else:
  292.                 ltmp = a.split(',')
  293.                 if len(ltmp) != 2:
  294.                     ltmp = [ltmp[0], ltmp[0]]
  295.                 fixed.append([ltmp[0].strip(), ltmp[1].strip()])
  296.  
  297.         self.d['__url__'] = fixed
  298.  
  299.  
  300.     def wrap_lines(self, reset = 0):
  301.  
  302.         lines = self.d['__bpydoc__'].split('\n')
  303.         self.content = []
  304.         newlines = []
  305.         newline = []
  306.  
  307.         if reset:
  308.             self.len_content = 0
  309.             self.spaces = 0
  310.  
  311.         for l in lines:
  312.             if l == '' and newline:
  313.                 newlines.append(newline)
  314.                 newline = []
  315.                 newlines.append('')
  316.             else: newline.append(l)
  317.         if newline: newlines.append(newline)
  318.  
  319.         for lst in newlines:
  320.             wrapped = text_wrap(" ".join(lst))
  321.             for l in wrapped:
  322.                 self.content.append(l)
  323.                 if l: self.len_content += 1
  324.                 else: self.spaces += 1
  325.  
  326.         if not self.content[-1]:
  327.             self.len_content -= 1
  328.  
  329.  
  330. # End of class BPy_Info
  331.  
  332. def parse_pyobj_close(closetag, lines, i):
  333.     i += 1
  334.     l = lines[i]
  335.     while l.find(closetag) < 0:
  336.         i += 1
  337.         l = "%s%s" % (l, lines[i])
  338.     return [l, i]
  339.  
  340. def parse_pyobj(var, lines, i):
  341.     "Bad code, was in a hurry for release"
  342.  
  343.     l = lines[i].replace(var, '').replace('=','',1).strip()
  344.  
  345.     i0 = i - 1
  346.  
  347.     if l[0] == '"':
  348.         if l[1:3] == '""': # """
  349.             if l.find('"""', 3) < 0: # multiline
  350.                 l2, i = parse_pyobj_close('"""', lines, i)
  351.                 if l[-1] == '\\': l = l[:-1]
  352.                 l = "%s%s" % (l, l2)
  353.         elif l[-1] == '"' and l[-2] != '\\': # single line: "..."
  354.             pass
  355.         else:
  356.             l = "ERROR"
  357.  
  358.     elif l[0] == "'":
  359.         if l[1:3] == "''": # '''
  360.             if l.find("'''", 3) < 0: # multiline
  361.                 l2, i = parse_pyobj_close("'''", lines, i)
  362.                 if l[-1] == '\\': l = l[:-1]
  363.                 l = "%s%s" % (l, l2)
  364.         elif l[-1] == '\\':
  365.             l2, i = parse_pyobj_close("'", lines, i)
  366.             l = "%s%s" % (l, l2)
  367.         elif l[-1] == "'" and l[-2] !=  '\\': # single line: '...'
  368.             pass
  369.         else:
  370.             l = "ERROR"
  371.  
  372.     elif l[0] == '(':
  373.         if l[-1] != ')':
  374.             l2, i = parse_pyobj_close(')', lines, i)
  375.             l = "%s%s" % (l, l2)
  376.  
  377.     elif l[0] == '[':
  378.         if l[-1] != ']':
  379.             l2, i = parse_pyobj_close(']', lines, i)
  380.             l = "%s%s" % (l, l2)
  381.  
  382.     return [l, i - i0]
  383.  
  384. # helper functions:
  385.  
  386. def parse_help_info(script):
  387.  
  388.     global PATHS, SCRIPT_INFO
  389.  
  390.     if script.userdir:
  391.         path = PATHS['uscripts']
  392.     else:
  393.         path = PATHS['scripts']
  394.  
  395.     fname = bsys.join(path, script.fname)
  396.  
  397.     if not bsys.exists(fname):
  398.         Draw.PupMenu('IO Error: couldn\'t find script %s' % fname)
  399.         return None
  400.  
  401.     f = file(fname, 'r')
  402.     lines = f.readlines()
  403.     f.close()
  404.  
  405.     # fix line endings:
  406.     if lines[0].find('\r'):
  407.         unixlines = []
  408.         for l in lines:
  409.             unixlines.append(l.replace('\r',''))
  410.         lines = unixlines
  411.  
  412.     llen = len(lines)
  413.     has_doc = 0
  414.  
  415.     doc_data = {
  416.         '__author__': '',
  417.         '__version__': '',
  418.         '__url__': '',
  419.         '__email__': '',
  420.         '__bpydoc__': '',
  421.         '__doc__': ''
  422.     }
  423.  
  424.     i = 0
  425.     while i < llen:
  426.         l = lines[i]
  427.         incr = 1
  428.         for k in doc_data.keys():
  429.             if l.find(k, 0, 20) == 0:
  430.                 value, incr = parse_pyobj(k, lines, i)
  431.                 exec("doc_data['%s'] = %s" % (k, value))
  432.                 has_doc = 1
  433.                 break
  434.         i += incr
  435.  
  436.     # fix these to seqs, simplifies coding elsewhere
  437.     for w in ['__author__', '__url__', '__email__']:
  438.         val = doc_data[w]
  439.         if val and type(val) == str:
  440.             doc_data[w] = [doc_data[w]]
  441.  
  442.     if not doc_data['__bpydoc__']:
  443.         if doc_data['__doc__']:
  444.             doc_data['__bpydoc__'] = doc_data['__doc__']
  445.  
  446.     if has_doc: # any data, maybe should confirm at least doc/bpydoc
  447.         info = BPy_Info(script, doc_data)
  448.         SCRIPT_INFO = info
  449.         return True
  450.  
  451.     else:
  452.         return False
  453.  
  454.  
  455. def parse_script_line(l):
  456.  
  457.     tip = 'No tooltip'
  458.     try:
  459.         pieces = l.split("'")
  460.         name = pieces[1].replace('...','')
  461.         data = pieces[2].strip().split()
  462.         version = data[0]
  463.         userdir = data[-1]
  464.         fname = data[1]
  465.         i = 1
  466.         while not fname.endswith('.py'):
  467.             i += 1
  468.             fname = '%s %s' % (fname, data[i])
  469.         if len(pieces) > 3: tip = pieces[3]
  470.     except:
  471.         return None
  472.  
  473.     return [name, int(version), fname, int(userdir), tip]
  474.  
  475.  
  476. def parse_bpymenus(lines):
  477.  
  478.     global AllGroups
  479.  
  480.     llen = len(lines)
  481.  
  482.     for i in range(llen):
  483.         l = lines[i].strip()
  484.         if not l: continue
  485.         if l[-1] == '{':
  486.             group = Group(l[:-2])
  487.             AllGroups.append(group)
  488.             i += 1
  489.             l = lines[i].strip()
  490.             while l != '}':
  491.                 if l[0] != '|':
  492.                     data = parse_script_line(l)
  493.                     if data:
  494.                         script = Script(data)
  495.                         group.add_script(script)
  496.                 i += 1
  497.                 l = lines[i].strip()
  498.  
  499. #    AllGroups.reverse()
  500.  
  501.  
  502. def create_group_menus():
  503.  
  504.     global AllGroups
  505.     menus = []
  506.  
  507.     for group in AllGroups:
  508.  
  509.         name = group.get_name()
  510.         menu = []
  511.         scripts = group.get_scripts()
  512.         for s in scripts: menu.append(s.name)
  513.         menu = "|".join(menu)
  514.         menu = "%s%%t|%s" % (name, menu)
  515.         menus.append([name, menu])
  516.  
  517.     return menus
  518.  
  519.  
  520. # Collecting data:
  521. fit_on_screen()
  522. parse_bpymenus(lines)
  523. GROUP_MENUS = create_group_menus()
  524.  
  525.  
  526. # GUI:
  527.  
  528. from Blender import BGL
  529. from Blender.Window import Theme
  530.  
  531. # globals:
  532.  
  533. START_SCREEN  = 0
  534. SCRIPT_SCREEN = 1
  535.  
  536. SCREEN = START_SCREEN
  537.  
  538. # gui buttons:
  539. len_gmenus = len(GROUP_MENUS)
  540.  
  541. BUT_GMENU = range(len_gmenus)
  542. for i in range(len_gmenus):
  543.     BUT_GMENU[i] = Draw.Create(0)
  544.  
  545. # events:
  546. BEVT_LINK  = None # range(len(SCRIPT_INFO.links))
  547. BEVT_EMAIL = None # range(len(SCRIPT_INFO.emails))
  548. BEVT_GMENU = range(100, len_gmenus + 100)
  549. BEVT_VIEWSOURCE = 1
  550. BEVT_EXIT = 2
  551. BEVT_BACK = 3
  552. BEVT_EXEC = 4    # Executes Script
  553.  
  554. # gui callbacks:
  555.  
  556. def gui(): # drawing the screen
  557.  
  558.     global SCREEN, START_SCREEN, SCRIPT_SCREEN
  559.     global SCRIPT_INFO, AllGroups, GROUP_MENUS
  560.     global BEVT_EMAIL, BEVT_LINK
  561.     global BEVT_VIEWSOURCE, BEVT_EXIT, BEVT_BACK, BEVT_GMENU, BUT_GMENU, BEVT_EXEC
  562.     global PADDING, WIN_W, WIN_H, SCROLL_DOWN, COLUMNS, FMODE
  563.  
  564.     theme = Theme.Get()[0]
  565.     tui = theme.get('ui')
  566.     ttxt = theme.get('text')
  567.  
  568.     COL_BG = float_colors(ttxt.back)
  569.     COL_TXT = ttxt.text
  570.     COL_TXTHI = ttxt.text_hi
  571.  
  572.     BGL.glClearColor(COL_BG[0],COL_BG[1],COL_BG[2],COL_BG[3])
  573.     BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
  574.     BGL.glColor3ub(COL_TXT[0],COL_TXT[1], COL_TXT[2])
  575.  
  576.     resize = screen_was_resized()
  577.     if resize: fit_on_screen()
  578.  
  579.     if SCREEN == START_SCREEN:
  580.         x = PADDING
  581.         bw = 85
  582.         bh = 25
  583.         hincr = 50
  584.  
  585.         butcolumns = (WIN_W - 2*x)/ bw
  586.         if butcolumns < 2: butcolumns = 2
  587.         elif butcolumns > 7: butcolumns = 7
  588.  
  589.         len_gm = len(GROUP_MENUS)
  590.         butlines = len_gm / butcolumns
  591.         if len_gm % butcolumns: butlines += 1
  592.  
  593.         h = hincr * butlines + 20
  594.         y = h + bh
  595.  
  596.         BGL.glColor3ub(COL_TXTHI[0],COL_TXTHI[1], COL_TXTHI[2])
  597.         BGL.glRasterPos2i(x, y)
  598.         Draw.Text('Scripts Help Browser')
  599.  
  600.         y -= bh
  601.  
  602.         BGL.glColor3ub(COL_TXT[0],COL_TXT[1], COL_TXT[2])
  603.  
  604.         i = 0
  605.         j = 0
  606.         for group_menu in GROUP_MENUS:
  607.             BGL.glRasterPos2i(x, y)
  608.             Draw.Text(group_menu[0]+':')
  609.             BUT_GMENU[j] = Draw.Menu(group_menu[1], BEVT_GMENU[j],
  610.                 x, y-bh-5, bw, bh, 0,
  611.                 'Choose a script to read its help information')
  612.             if i == butcolumns - 1:
  613.                 x = PADDING
  614.                 i = 0
  615.                 y -= hincr
  616.             else:
  617.                 i += 1
  618.                 x += bw + 3
  619.             j += 1
  620.  
  621.         x = PADDING
  622.         y = 10
  623.         BGL.glRasterPos2i(x, y)
  624.         Draw.Text('Select script for its help.  Press Q or ESC to leave.')
  625.  
  626.     elif SCREEN == SCRIPT_SCREEN:
  627.         if SCRIPT_INFO:
  628.  
  629.             if resize:
  630.                 SCRIPT_INFO.wrap_lines(1)
  631.                 SCROLL_DOWN = 0
  632.  
  633.             h = 18 * SCRIPT_INFO.len_content + 12 * SCRIPT_INFO.spaces
  634.             x = PADDING
  635.             y = WIN_H
  636.             bw = 38
  637.             bh = 16
  638.  
  639.             BGL.glColor3ub(COL_TXTHI[0],COL_TXTHI[1], COL_TXTHI[2])
  640.             for line in SCRIPT_INFO.header:
  641.                 y -= 18
  642.                 BGL.glRasterPos2i(x, y)
  643.                 size = Draw.Text(line)
  644.  
  645.             for line in text_wrap('Tooltip: %s' % SCRIPT_INFO.script.tip):
  646.                 y -= 18
  647.                 BGL.glRasterPos2i(x, y)
  648.                 size = Draw.Text(line)
  649.  
  650.             i = 0
  651.             y -= 28
  652.             for data in SCRIPT_INFO.d['__url__']:
  653.                 Draw.PushButton('link %d' % (i + 1), BEVT_LINK[i],
  654.                     x + i*bw, y, bw, bh, data[0])
  655.                 i += 1
  656.             y -= bh + 1
  657.  
  658.             i = 0
  659.             for data in SCRIPT_INFO.d['__email__']:
  660.                 Draw.PushButton('email', BEVT_EMAIL[i], x + i*bw, y, bw, bh, data[0])
  661.                 i += 1
  662.             y -= 18
  663.  
  664.             y0 = y
  665.             BGL.glColor3ub(COL_TXT[0],COL_TXT[1], COL_TXT[2])
  666.             for line in SCRIPT_INFO.content[SCROLL_DOWN:]:
  667.                 if line:
  668.                     line = line.replace('<br>', '')
  669.                     BGL.glRasterPos2i(x, y)
  670.                     Draw.Text(line)
  671.                     y -= 18
  672.                 else: y -= 12
  673.                 if y < PADDING + 20: # reached end, either stop or go to 2nd column
  674.                     if COLUMNS == 1: break
  675.                     elif x == PADDING: # make sure we're still in column 1
  676.                         x = 6*TEXT_WRAP + PADDING / 2
  677.                         y = y0
  678.  
  679.             x = PADDING
  680.             Draw.PushButton('source', BEVT_VIEWSOURCE, x, 17, 45, bh,
  681.                 'View this script\'s source code in the Text Editor (hotkey: S)')
  682.             Draw.PushButton('exit', BEVT_EXIT, x + 45, 17, 45, bh,
  683.                 'Exit from Scripts Help Browser (hotkey: Q)')
  684.             if not FMODE: 
  685.                 Draw.PushButton('back', BEVT_BACK, x + 2*45, 17, 45, bh,
  686.                 'Back to scripts selection screen (hotkey: ESC)')
  687.                 Draw.PushButton('run script', BEVT_EXEC, x + 3*45, 17, 60, bh, 'Run this script')
  688.  
  689.             BGL.glColor3ub(COL_TXTHI[0],COL_TXTHI[1], COL_TXTHI[2])
  690.             BGL.glRasterPos2i(x, 5)
  691.             Draw.Text('use the arrow keys or the mouse wheel to scroll text', 'small')
  692.  
  693. def fit_scroll():
  694.     global SCROLL_DOWN
  695.     if not SCRIPT_INFO:
  696.         SCROLL_DOWN = 0
  697.         return
  698.     max = SCRIPT_INFO.len_content + SCRIPT_INFO.spaces - 1
  699.     if SCROLL_DOWN > max: SCROLL_DOWN = max
  700.     if SCROLL_DOWN < 0: SCROLL_DOWN = 0
  701.  
  702.  
  703. def event(evt, val): # input events
  704.  
  705.     global SCREEN, START_SCREEN, SCRIPT_SCREEN
  706.     global SCROLL_DOWN, FMODE
  707.  
  708.     if not val: return
  709.  
  710.     if evt == Draw.ESCKEY:
  711.         if SCREEN == START_SCREEN or FMODE: Draw.Exit()
  712.         else:
  713.             SCREEN = START_SCREEN
  714.             SCROLL_DOWN = 0
  715.             Draw.Redraw()
  716.         return
  717.     elif evt == Draw.QKEY:
  718.         Draw.Exit()
  719.         return
  720.     elif evt in [Draw.DOWNARROWKEY, Draw.WHEELDOWNMOUSE] and SCREEN == SCRIPT_SCREEN:
  721.         SCROLL_DOWN += 1
  722.         fit_scroll()
  723.         Draw.Redraw()
  724.         return
  725.     elif evt in [Draw.UPARROWKEY, Draw.WHEELUPMOUSE] and SCREEN == SCRIPT_SCREEN:
  726.         SCROLL_DOWN -= 1
  727.         fit_scroll()
  728.         Draw.Redraw()
  729.         return
  730.     elif evt == Draw.SKEY:
  731.         if SCREEN == SCRIPT_SCREEN and SCRIPT_INFO:
  732.             load_script_text(SCRIPT_INFO.script)
  733.             return
  734.  
  735. def button_event(evt): # gui button events
  736.  
  737.     global SCREEN, START_SCREEN, SCRIPT_SCREEN
  738.     global BEVT_LINK, BEVT_EMAIL, BEVT_GMENU, BUT_GMENU, SCRIPT_INFO
  739.     global SCROLL_DOWN, FMODE
  740.  
  741.     if evt >= 100: # group menus
  742.         for i in range(len(BUT_GMENU)):
  743.             if evt == BEVT_GMENU[i]:
  744.                 group = AllGroups[i]
  745.                 index = BUT_GMENU[i].val - 1
  746.                 if index < 0: return # user didn't pick a menu entry
  747.                 script = group.get_scripts()[BUT_GMENU[i].val - 1]
  748.                 if parse_help_info(script):
  749.                     SCREEN = SCRIPT_SCREEN
  750.                     BEVT_LINK = range(20, len(SCRIPT_INFO.d['__url__']) + 20)
  751.                     BEVT_EMAIL = range(50, len(SCRIPT_INFO.d['__email__']) + 50)
  752.                     Draw.Redraw()
  753.                 else:
  754.                     res = Draw.PupMenu("No help available%t|View Source|Cancel")
  755.                     if res == 1:
  756.                         load_script_text(script)
  757.     elif evt >= 20:
  758.         if not WEBBROWSER:
  759.             Draw.PupMenu('Missing standard Python module%t|You need module "webbrowser" to access the web')
  760.             return
  761.  
  762.         if evt >= 50: # script screen email buttons
  763.             email = SCRIPT_INFO.d['__email__'][evt - 50][1]
  764.             webbrowser.open("mailto:%s" % email)
  765.         else: # >= 20: script screen link buttons
  766.             link = SCRIPT_INFO.d['__url__'][evt - 20][1]
  767.             webbrowser.open(link)
  768.     elif evt == BEVT_VIEWSOURCE:
  769.         if SCREEN == SCRIPT_SCREEN: load_script_text(SCRIPT_INFO.script)
  770.     elif evt == BEVT_EXIT:
  771.         Draw.Exit()
  772.         return
  773.     elif evt == BEVT_BACK:
  774.         if SCREEN == SCRIPT_SCREEN and not FMODE:
  775.             SCREEN = START_SCREEN
  776.             SCRIPT_INFO = None
  777.             SCROLL_DOWN = 0
  778.             Draw.Redraw()
  779.     elif evt == BEVT_EXEC: # Execute script
  780.         exec_line = ''
  781.         if SCRIPT_INFO.script.userdir:
  782.             exec_line = bsys.join(Blender.Get('uscriptsdir'), SCRIPT_INFO.script.fname)
  783.         else:
  784.             exec_line = bsys.join(Blender.Get('scriptsdir'), SCRIPT_INFO.script.fname)
  785.  
  786.         Blender.Run(exec_line)
  787.  
  788. keepon = True
  789. FMODE = False # called by Blender.ShowHelp(name) API function ?
  790.  
  791. KEYNAME = '__help_browser'
  792. rd = Registry.GetKey(KEYNAME)
  793. if rd:
  794.     rdscript = rd['script']
  795.     keepon = False
  796.     Registry.RemoveKey(KEYNAME)
  797.     for group in AllGroups:
  798.         for script in group.get_scripts():
  799.             if rdscript == script.fname:
  800.                 parseit = parse_help_info(script)
  801.                 if parseit == True:
  802.                     keepon = True
  803.                     SCREEN = SCRIPT_SCREEN
  804.                     BEVT_LINK = range(20, len(SCRIPT_INFO.d['__url__']) + 20)
  805.                     BEVT_EMAIL = range(50, len(SCRIPT_INFO.d['__email__']) + 50)
  806.                     FMODE = True
  807.                 elif parseit == False:
  808.                     Draw.PupMenu("ERROR: script doesn't have proper help data")
  809.                 break
  810.  
  811. if not keepon:
  812.     Draw.PupMenu("ERROR: couldn't find script")
  813. else:
  814.     Draw.Register(gui, event, button_event)
  815.